package org.jflame.captcha.web;

import org.jflame.captcha.Captcha;
import org.jflame.captcha.creator.CaptchaCreator;
import org.jflame.captcha.creator.SimpleImage;
import org.jflame.captcha.creator.factory.CaptchaCreatorFactory;
import org.jflame.captcha.validator.Verifiable;
import org.jflame.commons.config.ServletParamConfig;
import org.jflame.commons.file.MimeTypes;
import org.jflame.commons.json.JsonHelper;
import org.jflame.commons.key.IDHelper;
import org.jflame.commons.model.CallResult;
import org.jflame.commons.model.CallResult.ResultEnum;
import org.jflame.commons.util.IOHelper;
import org.jflame.commons.util.StringHelper;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 生成随机验证码图片servlet.
 * 
 * @author charles.zhang
 */
@SuppressWarnings("rawtypes")
public class CaptchaServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private CaptchaCreator creator;
    private String captchaType;

    public CaptchaServlet() {
        super();
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        ServletParamConfig servletParam = new ServletParamConfig(config);
        // 一个servlet配置一个类型
        captchaType = servletParam.getStringOrDefault(CaptchaCreatorFactory.KEY_TYPE.getName(), "simple_image");
        if (StringHelper.isEmpty(captchaType)) {
            throw new ServletException("未配置验证码类型");
        }
        creator = CaptchaCreatorFactory.build(captchaType, servletParam);
    }

    /**
     * 生成验证码
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String paramCodeName = request.getParameter("id");
        String codeName = paramCodeName != null ? paramCodeName : "valid_code";

        HttpSession session = request.getSession();
        Captcha captcha = creator.create();
        if (!(captcha.getDisplay() instanceof SimpleImage)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "不支持的验证码类型");
            return;
        }
        ServletOutputStream sos = response.getOutputStream();
        SimpleImage display = (SimpleImage) captcha.getDisplay();
        setDisableCacheHeader(response);
        session.setAttribute(codeName, captcha.getVerifiable());
        if (display.isOutJson()) {
            response.setContentType("application/json");
        } else {
            response.setContentType(MimeTypes.getInstance()
                    .getMimetypeByExt(display.getImageFormat()));
        }
        display.write(sos);
        sos.close();
    }

    void setDisableCacheHeader(HttpServletResponse response) {
        response.setDateHeader("Expires", 1L);
        response.addHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache, no-store, max-age=0");
    }

    /**
     * 校验验证码
     */
    @SuppressWarnings("unchecked")
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = request.getSession(false);
        if (session == null) {
            outJson(resp, JsonHelper.toJson(CallResult.createFailed("验证码验证失败")));
        }
        String paramCodeName = request.getParameter("id");
        String codeName = paramCodeName != null ? paramCodeName : "valid_code";
        Verifiable real = (Verifiable) session.getAttribute(codeName);
        CallResult<String> result = new CallResult<>(ResultEnum.PARAM_ERROR.getStatus(), "验证码错误");
        if (real != null) {
            // 先从请求参数captcha获取再从request body读
            String captcha = request.getParameter("captcha");
            if (StringHelper.isEmpty(captcha)) {
                captcha = IOHelper.readText(request.getInputStream(),
                        request.getCharacterEncoding() == null ? StandardCharsets.UTF_8.name()
                                : request.getCharacterEncoding());
            }
            if (StringHelper.isNotEmpty(captcha)) {
                Verifiable input = creator.parse(captcha);
                if (real.verify(input)) {
                    // 给个返回值,用于标识验证已通过
                    String cr = codeName + IDHelper.millisAndRandomNo(3);
                    session.setAttribute(codeName + "_checkresult", cr);
                    result.success(cr);
                }
            }
        }
        outJson(resp, result);
    }

    void outJson(HttpServletResponse response, Object content) throws IOException {
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType("application/json");
        response.getWriter()
                .print(content instanceof String ? content : JsonHelper.toJson(content));
    }

}
